/*
 * Copyright (c) Tecplot, Inc.
 *
 * All rights reserved.  Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer. 
 *   - Redistributions in binary form must reproduce the above copyright notice, this list
 *     of conditions and the following disclaimer in the documentation and/or other
 *     materials provided with the distribution.
 *   - Neither the name of the Tecplot, Inc., nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "TecFile.h"
#include "toolbox/tptoolbox.h"
#include "toolbox/ArgList.h"
#include <sstream>
#include <algorithm>
#include <iterator>

namespace tbx=tecplot::toolbox;
using std::string;

/**
 */
TecFile::TecFile(const std::string&              dataSetTitle,
                 const std::vector<std::string>& variables,
                 const std::string&              fileName,
                 const std::string&              scratchDirectory,
                 DataType                        dataType) :
    m_isClosed(false),
    m_numValuesWritten(0)
{
    REQUIRE(!dataSetTitle.empty());
    REQUIRE(!variables.empty());
    REQUIRE(!fileName.empty());
    REQUIRE(!scratchDirectory.empty());

    bool dataIsDouble = dataType == Double;

    // Convert variable name vector into comma separated list
    std::ostringstream varNames;
    std::copy(variables.begin(), variables.end(),
              std::ostream_iterator<string>(varNames, ","));

    tbx::ArgList argList;
    argList.appendString(SV_DATASETTITLE,  dataSetTitle);
    argList.appendString(SV_VARIABLES,     varNames.str().c_str());
    argList.appendString(SV_FILENAME,      fileName.c_str());
    argList.appendString(SV_SCRATCHDIR,    scratchDirectory.c_str());
    argList.appendInt   (SV_VISDOUBLE,     dataIsDouble);
#ifdef DEBUG
    argList.appendInt(SV_DEBUG, TRUE);
#endif

    LgIndex_t result = TecUtilTecIniX(argList.getRef()); // Returns zero if successful

    if (result != 0)
        throw Error("Unable to create Tecplot file.");
}

/**
 */
TecFile::~TecFile()
{
    if (!m_isClosed)
    {
        try
        {
            close();
        }
        catch (Error&)
        {
            // Swallow the exception in the destructor
        }
    }
}

/**
 */
void TecFile::close()
{
    m_isClosed = true;
    LgIndex_t result = TecUtilTecEnd();
    if (result != 0)
        throw Error("Unable to close Tecplot file.");
}

/**
 */
void TecFile::zone(const std::string& name,
                   ZoneType_e         zoneType,
                   LgIndex_t          numNodes,
                   LgIndex_t          numElements,
                   DataFormat         dataFormat,
                   Strand_t           strandID,
                   double             solutionTime)
{
    REQUIRE(VALID_ENUM(zoneType, ZoneType_e));
    REQUIRE(numNodes > 0);

    tbx::ArgList argList;
    if (!name.empty())
        argList.appendString(SV_TITLE,      name.c_str());
    argList.appendInt(SV_ZONETYPE,     zoneType);
    argList.appendInt(SV_IMAX,         numNodes);
    argList.appendInt(SV_JMAX,         numElements);
    argList.appendInt(SV_KMAX,         1);
    argList.appendInt(SV_ISBLOCK,      dataFormat == Block);
    argList.appendInt(SV_STRANDID,     strandID);
    argList.appendDouble(SV_SOLUTIONTIME, solutionTime);

    LgIndex_t result = TecUtilTecZneX(argList.getRef());

    if (result != 0)
        throw Error("Unable to create Tecplot file.");
}

/**
 */
void TecFile::data(const std::vector<float>& dataArray)
{
    if (!dataArray.empty())
    {
        static LgIndex_t isDouble  = FALSE;
        LgIndex_t        numValues = static_cast<LgIndex_t>(dataArray.size());
        LgIndex_t result = TecUtilTecDat(&numValues,
                                         (void*)(&dataArray[0]),
                                         &isDouble);
        if (result != 0)
            throw Error("Unable to write data to Tecplot file.");
        m_numValuesWritten += dataArray.size();
    }
}

/**
 */
void TecFile::connectivity(const std::vector<LgIndex_t>& dataArray)
{
    if (!dataArray.empty())
    {
        bool success = TecUtilTecNod(const_cast<LgIndex_t*>(&dataArray[0])) == 0;
        if (!success)
            throw Error("Unable to write connectivity to Tecplot file.");
    }
}


/**
 */
TecFile::Error::Error(const std::string& message) :
    m_errorMessage(message)
{
}

/**
 */
const char* TecFile::Error::what() throw()
{
    return m_errorMessage.c_str();
}

TecFile::Error::~Error() throw()
{
}
